/**
 * 
 */
package info.niwota.group;

import info.niwota.group.contact.GroupContacts;

import java.util.Set;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.Contacts;

/**
 * 
 * @author qiangli
 * 
 */
// cd /data/data/com.android.providers.contacts/databases
// sqlite3 contacts.db
public abstract class ContactAdapter {
	private static ContactAdapter adapter;
	
	//private static final String ADAPTER_1x = "info.niwota.group.ContactAdapter1";

	//private static final String ADAPTER_2x = "info.niwota.group.ContactAdapter2";

	private static final int VERSION_CODES_ECLAIR = 5;

	static final String TAG = "ContactAdapter";

	public static ContactAdapter getAdapter() {
		if (adapter == null) {
			try {
//				String cn = (Integer.parseInt(Build.VERSION.SDK) >= VERSION_CODES_ECLAIR ? ADAPTER_2x
//						: ADAPTER_1x);
//
//				Class<? extends ContactAdapter> clz = Class.forName(cn)
//						.asSubclass(ContactAdapter.class);
//				adapter = clz.newInstance();
				
				adapter = (newContactAPI ? new ContactAdapter2() : new ContactAdapter1());
				
				//Log.d(TAG, "class: " + adapter.getClass().getName() +" adapter: " + adapter);
			} catch (Exception e) {
				throw new IllegalStateException(e);
			}
		}
		return adapter;
	}

	public static boolean newContactAPI = (Integer.parseInt(Build.VERSION.SDK) >= VERSION_CODES_ECLAIR);

	/**
	 * Load contact and group, shadow/ignore entries in group with identical names
	 *
	 * @param context
	 * @param contacts
	 */
	public void loadEmailContacts(Context ctx, Set<EmailContact> contacts) {
			//load from contact first and shadow entries in group with same name (if any)
			loadContactsInConact(ctx, contacts);
			//group contact
			GroupContacts.loadGroups(ctx, contacts);
	}

	protected abstract void loadContactsInConact(Context ctx, Set<EmailContact> contacts);

	/**
	 * Load aggregated list of addresses from both group and contact
	 * 
	 * @param context
	 * @param addresses
	 */
	public void loadEmailAddresses(Context ctx, Set<EmailAddress> addresses) {
		//load from contact first as it may have a better name
		loadAddressesInConact(ctx, addresses);
		//load from group
		GroupContacts.loadAddresses(ctx, addresses);
	}
	
	protected abstract void loadAddressesInConact(Context ctx, Set<EmailAddress> addresses);

	/**
	 * Create local list
	 * 
	 * @param context
	 * @param name
	 * @param emails
	 * @param star 
	 */
	public void addList(Context ctx, String name, int star, String[] emails) {
		GroupContacts.addGroup(ctx, name, star, emails);
	}

	/**
	 * Create list as contact
	 * 
	 * @param context
	 * @param name
	 * @param star 
	 * @param emails
	 */
	public abstract void saveAsContact(Context context, String name, int star, String[] emails);

	/**
	 * Load aggregated lists from both contact and group
	 * 
	 * @param context
	 * @param lists
	 */
	public void loadListNames(Context ctx, Set<String> names) {
		//TODO get rid of lists without email addresses?
		//load from both contact and group
		loadGroupNamesInConact(ctx, names);
		GroupContacts.loadGroupNames(ctx, names);
	}
	
	protected abstract void loadGroupNamesInConact(Context ctx, Set<String> names);

	/**
	 * Load group or contact photo
	 * @param context
	 * @param id
	 * @param res
	 * @return
	 */
	public Bitmap loadPhoto(Context ctx, long id, int res) {
		if (id == -1) {
			return BitmapFactory.decodeResource(ctx.getResources(), res);
		}
		if (isGroup(id)) {
			return loadGroupPhoto(ctx, id, res);
		} else {
			return loadContactPhoto(ctx, id, res);
		}
	}
	
	protected abstract Bitmap loadContactPhoto(Context ctx, long id, int res);

	private Bitmap loadGroupPhoto(Context ctx, long id, int res) {
		//TODO  group photo
		return BitmapFactory.decodeResource(ctx.getResources(), res);
	}

	private boolean isGroup(long id) {
		return (id < -1);
	}

	/**
	 * Add email to group or contact
	 * @param context
	 * @param id
	 * @param emails
	 */
	public void addEmail(Context ctx, long id, String[] emails) {
		if (isGroup(id)) {
			GroupContacts.addEmailToGroup(ctx, id, emails);
		} else {
			addEmailToContact(ctx, id, emails);
		}
	}
	
	protected abstract void addEmailToContact(Context ctx, long id, String[] emails);

	/**
	 * Check email in contact and group
	 * @param context
	 * @param email
	 * @return
	 */
	public boolean hasEmail(Context ctx, String email) {
		//check contact first as this results in better performance
		//1. if we have one, most likely it is found in contacts
		//2. group stores emails as one string, it is a sequential lookup
		if (hasEmailInContact(ctx, email)) {
			return true;
		}
		return GroupContacts.hasEmailInGroup(ctx, email);
	}
	
	protected abstract boolean hasEmailInContact(Context ctx, String email);

	/**
	 * Get email from contact first, then from group if name is a group name and it is not found in contact
	 * @param context
	 * @param name
	 * @return
	 */
	public String[] getEmails(Context ctx, String name) {
		if (name == null) {
			return null;
		}
		String[] emails = getEmailsInContact(ctx, name);
		if (emails == null && name.startsWith("@")) {
			return GroupContacts.getEmails(ctx, name);
		} 
		return emails;
	}
	
	protected abstract String[] getEmailsInContact(Context ctx, String name);

	/**
	 * Get group or contact id
	 * @param context
	 * @param name
	 * @return
	 */
	public long getId(Context ctx, String name) {
		if (name == null) {
			return -1;
		}
		long id = getContactId(ctx, name);
		if (id == -1 && name.startsWith("@")) {
			return GroupContacts.getIdByName(ctx, name);
		}
		return id;
	}
	
	protected abstract long getContactId(Context ctx, String name);

	public abstract Intent getIntent();

	public static Uri buildContactUri(long contactId) {
		Uri uri = (newContactAPI ? ContactsContract.RawContacts.CONTENT_URI :  Contacts.People.CONTENT_URI);
		return uri.buildUpon().appendPath(contactId + "").build();
	}

	public void removeList(Context ctx, long id) {
		if (isGroup(id)) {
			GroupContacts.removeGroup(ctx, id);
		}
	}

	public void starList(Context ctx, long id, int starred) {
		if (isGroup(id)) {
			GroupContacts.starGroup(ctx, id, starred);
		}
	}
}
